
/*  1.  Promise 是一个类，实例化这个类时，需要传递一个执行器函数进去 ，这个函数会立即执行
        这个函数接收两个参数，resolve reject
        new Promise((resolve, reject)=>{

        })

    2.  Promise 中有三种状态，分别为 fulfilled 成功  rejected 失败  pending 等待
        只能从 pending 变为 fulfilled 或者 从 pending 变为 rejected
        pending ——> fulfilled
        pending ——> rejected
        一旦状态确定就不可更改

    3.  resolve 和 reject 函数是用来更改状态
        resolve：fulfilled
        reject：rejected

        new Promise((resolve, reject)=>{
            resolve('成功')
            reject('失败')
        })

    4.  then方法接收两个参数，一个是成功之后的回调，一个失败之后的回调
        then方法内部做的事情就是判断状态，如果状态是成功，则调用传入的成功 如果是失败，则调用失败之后的回调
        then方法是定义在原型对象中的

        let promise = new Promise((resolve, reject)=>{
            resolve('成功')
            // reject('失败')
        })
        promise.then(() => {
            // 成功之后的回调函数
        }, () => {
            // 失败之后的回调函数
        })

    5.  then成功回调接收一个参数 表示成功之后的值
        then失败回调接收一个参数 表示失败之后的原因

        let promise = new Promise((resolve, reject)=>{
            resolve('成功')
            // reject('失败')
        })
        promise.then((value) => {
            // 成功之后的回调函数接收一个参数 value
            // 这个 value 是 resolve 函数执行时传入的
        }, (reason) => {
            // 失败之后的回调函数接收一个参数 reason
            // 这个 reason 是 reject 函数执行时传入的
        })
*/

const PENDING = 'pending';// 等待
const FULFILLED = 'fulfilled';// 成功
const REJECTED = 'rejected';// 失败
class MyPromise {
    constructor (executor) {
        try {
            // executor 实例化 MyPromise时传递执行器函数，会立即执行
            executor(this.resolve, this.reject);
        } catch (e) {
            // 捕获执行器函数执行过程中的错误
            this.reject(e)
        }
      
    }

    // 实例属性 status 表示当前promise的状态 （定义成实例属性，是因为每个promise实例都有自己的状态）
    status = PENDING

    // 实例属性 value 用于存储成功之后的值（定义成实例属性，是因为每个promise实例都有自己成功之后的值）
    value = undefined

    // 实例属性 reason 用于存储失败之后的原因 （定义成实例属性，是因为每个promise实例都有自己失败之后的值）
    reason = undefined

    // 实例属性 successCallback 表示成功回调 用于存储异步情况下（当前是等待状态）的成功回调
    // 需要在异步操作结束也就是resolve函数调用时执行
    // successCallback = undefined

    // 改为数组存储，因为then多次调用会有多个回调函数
    successCallback = []

    // 实例属性 failCallback 表示失败回调 用于存储异步情况下（当前是等待状态）的失败回调
    // 需要在异步操作结束也就是reject函数调用时执行
    // failCallback = undefined

    // 改为数组存储，因为then多次调用会有多个回调函数
    failCallback = []

    // 实例方法 resolve  作用将状态改为成功
    resolve = value => {
        // 此处使用箭头函数，是为了使this指向实例对象
        // 如果状态不是等待，阻止程序向下执行（因为一旦状态确定就不可更改）
        // 将状态改为成功
        if (this.status !== PENDING) {
            return;
        }
        this.status = FULFILLED;

        // 保存成功之后的值
        // value 用来接收 resolve 函数执行时传入的实参数据
        this.value = value;

        // this.successCallback存储是异步操作时（此时是pending等待状态）传入的then方法的成功回调
        // 我们会在异步操作成功时调用resolve函数，此时表示异步操作已经结束，我们需要调用成功回调this.successCallback
        // 判断成功回调是否存在，如果存在 调用
        // this.successCallback &&  this.successCallback(this.value);

        while (this.successCallback.length) {
            this.successCallback.shift()(this.value)
        }
    }
    // 实例方法 reject  作用将状态改为失败
    reject = reason => {
        // 此处使用箭头函数，是为了使this指向实例对象

        // 如果状态不是等待，阻止程序向下执行（因为一旦状态确定就不可更改）
        // 将状态改为失败
        if (this.status !== PENDING) {
            return;
        }
        this.status = REJECTED;

        // 保存失败后的原因
        // reason 用来接收 reject 函数执行时传入的实参数据
        this.reason = reason;

        // this.failCallback存储是异步操作时（此时是pending等待状态）传入的then方法的失败回调
        // 我们会在异步操作失败时调用reject函数，此时表示异步操作已经结束，我们需要调用失败回调this.failCallback
        // 判断失败回调是否存在，如果存在 调用
        // this.failCallback &&  this.failCallback(this.reason);

        while (this.failCallback.length) {
            // this.failCallback.shift()(this.reason) 
            // (这块注释的原因是，then方法中等待状态的回调函数调用方式改写了，then方法中等待状态的回调函数在调用时已经传递了参数，所以这块就不需要再传递参数了)

            this.failCallback.shift()()
        }

       
    }

    // 原型方法
    // then成功回调接收一个参数 表示成功之后的值
    // then失败回调接收一个参数 表示失败之后的原因
    then(successCallback, failCallback) {
        // 调用then方法时，如果不传递参数，promise的状态会依次向后传递
        // 为了使promise的状态依次向后传递，这里我们需要自己补一个函数作为参数
        // 我们自己补的失败回调需要手动抛出一个错误，这样错误状态就能传递给下一个promise
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => { throw reason };

        // 实现then方法链式调用需要返回一个新的promise实例
        let promise2 = new MyPromise((resolve, reject) => {
            // 判断状态
            if (this.status === FULFILLED) {
                // 当前状态是成功
                console.log('执行了then方法，此时的状态是成功')
                // 使用setTimeout变成异步代码，确保能拿到promise2
                setTimeout(() => {
                    try {
                        // 传入实参  this.value 成功之后的值
                        // 变量 x 用来存储前一个成功回调的返回值
                        let x = successCallback(this.value)

                        // // 将前一个成功回调的返回值传递给下一个then方法的成功回调
                        // resolve(x);

                        // 判断 x 的值是普通值还是promise对象
                        // 如果是普通值 直接调用resolve
                        // 如果是promise对象 查看promise对象的结果
                        // 再根据promise对象的结果 决定调用resolve 还是调用reject
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        // 捕获到前一个then方法传入的成功回调中的错误，传递给下一个then方法的失败回调
                        // 也就是捕获前一个promise的错误，传递给下一个promise
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                // 当前状态是失败
                console.log('执行了then方法，此时的状态是失败')

                // 使用setTimeout变成异步代码，确保能拿到promise2
                setTimeout(() => {
                    try {
                        // 传入实参 this.reason 失败之后的原因
                        // 变量 x 用来存储前一个失败回调的返回值
                        let x = failCallback(this.reason)

                        // 判断 x 的值是普通值还是promise对象
                        // 如果是普通值 直接调用resolve
                        // 如果是promise对象 查看promise对象的结果
                        // 再根据promise对象的结果 决定调用resolve 还是调用reject
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        // 捕获到前一个then方法传入的失败回调中的错误，传递给下一个then方法的失败回调
                        // 也就是捕获前一个promise的错误，传递给下一个promise
                        reject(e)
                    }
                
                },0)
              
            } else {
                // 当前状态是等待  
                // 异步情况下，当前的状态就是等待  
                console.log('执行了then方法，此时的状态是等待')

                // 将成功回调和失败回调存储起来
                // 存储传入的回调是为了在异步操作结束时调用
                // this.successCallback = successCallback;
                // this.failCallback = failCallback;
    
                // 改为数组存储，因为then多次调用会有多个回调函数 (这块注释的原因是 这么写无法捕获这里成功回调和失败回调中的错误，需要改写一下)
                // this.successCallback.push(successCallback)
                // this.failCallback.push(failCallback)


                this.successCallback.push(() => {
                    // 使用setTimeout变成异步代码，确保能拿到promise2
                    setTimeout(() => {
                        try {
                            // 传入实参  this.value 成功之后的值
                            // 变量 x 用来存储前一个成功回调的返回值
                            let x = successCallback(this.value)
    
                            // // 将前一个成功回调的返回值传递给下一个then方法的成功回调
                            // resolve(x);
    
                            // 判断 x 的值是普通值还是promise对象
                            // 如果是普通值 直接调用resolve
                            // 如果是promise对象 查看promise对象的结果
                            // 再根据promise对象的结果 决定调用resolve 还是调用reject
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            // 捕获到前一个then方法传入的成功回调中的错误，传递给下一个then方法的失败回调
                            // 也就是捕获前一个promise的错误，传递给下一个promise
                            reject(e)
                        }
                    
                    },0)
                })

                this.failCallback.push(() => {
                    // 使用setTimeout变成异步代码，确保能拿到promise2
                    setTimeout(() => {
                        try {
                            // 传入实参 this.reason 失败之后的原因
                            // 变量 x 用来存储前一个失败回调的返回值
                            let x = failCallback(this.reason)

                            // 判断 x 的值是普通值还是promise对象
                            // 如果是普通值 直接调用resolve
                            // 如果是promise对象 查看promise对象的结果
                            // 再根据promise对象的结果 决定调用resolve 还是调用reject
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            // 捕获到前一个then方法传入的失败回调中的错误，传递给下一个then方法的失败回调
                            // 也就是捕获前一个promise的错误，传递给下一个promise
                            reject(e)
                        }
                    
                    },0)
                })
            }
        })
       
        return promise2
    }
    // 原型方法 finally 方法有两个特点 
    // 1. 无论当前的promise最终的状态成功还是失败，finally方法中的回调都会执行
    // 2. finally方法后面可以链式调用拿到当前promise对象最终返回的结果
    finally (callback) {
        // 调用then方法得到当前promise的状态
        return this.then(value => {
            // 把当前then方法成功的返回值value传递到下一个then方法的成功回调
            // callback()
            // return value
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            // 把当前then方法失败的原因reason传递到下一个then方法的失败回调
            // callback()
            // throw reason
            return MyPromise.resolve(callback()).then(() => {throw reason})
        })
    }
    // 原型方法 catch方法
    catch (failCallback) {
        return this.then(undefined, failCallback)
    } 
    // 静态方法 all
    static all (array) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject)=>{
            function addData (key, value) {
                result[key] = value;
                index++;
                if (index === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    // promise对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值
                    addData(i, array[i])
                }
            }
        })
    }
    // 静态方法 resolve 把给定的值转换成promise对象
    static resolve (value) {
        if ( value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise(resolve => resolve(value))
               
        }
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        // x（上一个then方法返回值）是 promise 对象
        
        // x.then( value => {
        //     // 在这个promise实例成功后把值传递给下一个then方法
        //     resolve(value)
        // }, reason => {
        //     // 在这个promise实例失败后把失败原因传递给下一个then方法
        //     reject(reason)
        // })

        // 等价于上述写法
        x.then(resolve, reject);  
    } else {
        // x 是普通值
        resolve(x)
    }
}